/*
 * AbstractProfiler.cc
 *
 *  Created on: 2011-04-11
 *      Author: Jagger
 */

#include "AbstractProfiler.h"

void AbstractProfiler::initialize()
{
	maxQueueSize = par("maxQueueSize");

	// PARAMETRY DO STATYSTYK
	bandwidthEvent = new cMessage("bandwidthEvent");

	inputSignal = registerSignal("Input");
	outputSignal = registerSignal("Output");

	bandwidthCalcTick = par("bandwidthCalcTick");
	endTime = par("endTime");

	inputBandwidthSum = 0;
	outputBandwidthSum = 0;

	inputBandwidth = 0;
	outputBandwidth = 0;

	emit(inputSignal, inputBandwidth);
	emit(outputSignal, outputBandwidth);

	scheduleAt(SIMTIME_ZERO, bandwidthEvent);

	WATCH(inputBandwidth);
	WATCH(outputBandwidth);
	// KONIEC PARAMETROW DO STATYSTYK
}

void AbstractProfiler::handleMessage(cMessage* msg)
{
	if(msg == bandwidthEvent)
	{
		// Co okreslony czas obliczamy srednie zuzycie przepustowosci na wejsciu
		// i wyjsciu profilera.
		calcMeanBandwidth();

		simtime_t currentTime = simTime();
		if(currentTime < endTime || endTime == 0 || !queue.empty())
			scheduleAt(currentTime + bandwidthCalcTick, bandwidthEvent);
		else
		{
			inputBandwidth = 0;
			outputBandwidth = 0;
			delete bandwidthEvent;
		}
	}
	else
	{
		NetPacket* pck = check_and_cast<NetPacket*>(msg);

		if(!pck->isSelfMessage())
		{
			// Zaznaczamy przybycie pakietu wzrostem zuzycia przepustowosci
			//na wejsciu.
			inputBandwidthSum += pck->getByteLength();

			// Odrzucamy pakiety ktorych nie mozemy juz przyjac
			if(canReceive())
			{
				// Otrzymalismy pakiet z zewnatrz.
				// Umieszczamy go w kolejce i, jesli jest w niej pierwszy,
				// obslugujemy go.
				if(queue.empty())
				{
					simtime_t processDelay = par("processDelay");
					scheduleAt(simTime() + processDelay, pck);
				}

				queue.push_back(pck);
			}
			else
			{
				EV << "Packet " << pck->getName() << " discarded.\n";
				delete pck;
			}
		}
		else
		{
			// Nadszedl czas obslugi pakietu znajdujacego sie na poczatku kolejki.
			// Ponownie weryfikujemy czy pakiet jest gotowy do wyslania.
			simtime_t delay;
			if(acceptPacket(pck, delay))
			{
				// Jesli tak to przepuszczamy go dalej.
				queue.pop_front();

				cGate* out = gate("out");
				cChannel* channel = out->getChannel();
				if(channel)
				{
					simtime_t finishTime = channel->getTransmissionFinishTime();
					simtime_t delay = std::max(finishTime - simTime(), SIMTIME_ZERO);

					sendDelayed(pck, delay, out);
				}
				else
					send(pck, out);

				// Zaznaczamy wyslanie pakietu wzrostem zuzycia przepustowosci
				// na wyjsciu.
				outputBandwidthSum += pck->getByteLength();

				// Jesli w kolejce istnieja jeszcze oczekujace pakiety to
				// kontynuujemy ich przetwarzanie.
				if(!queue.empty())
				{
					simtime_t processDelay = par("processDelay");
					scheduleAt(simTime() + processDelay, queue.front());
				}
			}
			else
			{
				// Jesli pakiet nie spelnia warunkow okreslonych przez algorytm
				// profilowania, to delegujemy dla niego kolejne sprawdzenie w
				// przyszlosci.
				simtime_t processDelay = par("processDelay");
				scheduleAt(simTime() + delay + processDelay, pck);
			}
		}
	}
}

bool AbstractProfiler::canReceive()
{
	return queue.size() < maxQueueSize || !maxQueueSize;
}

void AbstractProfiler::calcMeanBandwidth()
{
	inputBandwidth = inputBandwidthSum / bandwidthCalcTick.dbl();
	outputBandwidth = outputBandwidthSum / bandwidthCalcTick.dbl();

	emit(inputSignal, inputBandwidth);
	emit(outputSignal, outputBandwidth);

	inputBandwidthSum = 0;
	outputBandwidthSum = 0;
}
